package com.zh.authserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.security.web.context.SecurityContextHolderFilter;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class AuthServerConfig {

    // 配置授权服务器设置
    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

    // 授权服务器安全过滤器链配置
    // 用于处理 OAuth2 协议核心流程（授权、令牌发放等）
    // 优先级高于基础安全过滤器链
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        // 创建授权服务器配置器
        var authServerConfigurer = new OAuth2AuthorizationServerConfigurer();



        // 配置授权服务器
        http.with(authServerConfigurer, configurer -> {
            // 启用 OpenID Connect 1.0 支持
            configurer.oidc(Customizer.withDefaults());

//            //自定义Token端点
//                .tokenEndpoint((tokenEndpoint) -> tokenEndpoint.accessTokenRequestConverter(
//                        new DelegatingAuthenticationConverter(Arrays.asList(
//                                new OAuth2AuthorizationCodeAuthenticationConverter(),
//                                new OAuth2RefreshTokenAuthenticationConverter(),
//                                new OAuth2ClientCredentialsAuthenticationConverter(),
//                                new OAuth2ResourceOwnerPasswordAuthenticationConverter()))
//                ));

        });

        // 配置安全规则
        http.securityMatcher(authServerConfigurer.getEndpointsMatcher())
                //添加自定义的filter
                .addFilterBefore(new IPWhitelistFilter("authorization"), SecurityContextHolderFilter.class)
                .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
                // 配置 OAuth2 资源服务器支持 JWT
                .oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults()))
                // 关键配置：为授权码模式配置认证入口点。当未认证的请求访问需要认证的端点时，重定向到登录页
                .exceptionHandling(exceptions -> exceptions
                        .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
                );
//                .csrf(csrf -> csrf.ignoringRequestMatchers(authorizationServerConfigurer.getEndpointsMatcher()));

        return http.build();
    }

// 基础安全过滤器链：
// 用于处理用户登录和认证、会话管理等基础认证功能
@Bean
@Order(1)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(authorize -> authorize
                    // 仅允许匿名访问登录页，其他请求必须认证
                    .requestMatchers("/login", "/login?error", "/hello").permitAll()

                    .anyRequest().authenticated()
            )
            .formLogin(form -> form
                    // 让Spring Security自动处理登录成功后的重定向
                    // 不要硬编码到/oauth2/authorize，否则会丢失OAuth2流程中的参数
                    .permitAll()
            )
            .logout(logout -> logout
                    // 配置登出逻辑
                    .logoutSuccessUrl("/login?logout")
                    .permitAll()
            );
    return http.build();
}

    // 用户详情服务
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("guest")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
                .build();

        UserDetails admin = User.withUsername("admin")
                .password(passwordEncoder().encode("123456"))
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 配置 JWK 源
    @Bean
    public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // @formatter:off
        RSAKey rsaKey= new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }


}